/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 *
 * Class: mxCellStatePreview
 *
 * Implements a live preview for moving cells.
 *
 * Constructor: mxCellStatePreview
 *
 * Constructs a move preview for the given graph.
 *
 * Parameters:
 *
 * graph - Reference to the enclosing <mxGraph>.
 */
function mxCellStatePreview(graph)
{
    this.deltas = new mxDictionary();
    this.graph = graph;
};

/**
 * Variable: graph
 *
 * Reference to the enclosing <mxGraph>.
 */
mxCellStatePreview.prototype.graph = null;

/**
 * Variable: deltas
 *
 * Reference to the enclosing <mxGraph>.
 */
mxCellStatePreview.prototype.deltas = null;

/**
 * Variable: count
 *
 * Contains the number of entries in the map.
 */
mxCellStatePreview.prototype.count = 0;

/**
 * Function: isEmpty
 *
 * Returns true if this contains no entries.
 */
mxCellStatePreview.prototype.isEmpty = function()
{
    return this.count == 0;
};

/**
 * Function: moveState
 */
mxCellStatePreview.prototype.moveState = function(state, dx, dy, add, includeEdges)
{
    add = (add != null) ? add : true;
    includeEdges = (includeEdges != null) ? includeEdges : true;

    var delta = this.deltas.get(state.cell);

    if (delta == null)
    {
        // Note: Deltas stores the point and the state since the key is a string.
        delta = {point: new mxPoint(dx, dy), state: state};
        this.deltas.put(state.cell, delta);
        this.count++;
    }
    else if (add)
    {
        delta.point.x += dx;
        delta.point.y += dy;
    }
    else
    {
        delta.point.x = dx;
        delta.point.y = dy;
    }

    if (includeEdges)
    {
        this.addEdges(state);
    }

    return delta.point;
};

/**
 * Function: show
 */
mxCellStatePreview.prototype.show = function(visitor)
{
    this.deltas.visit(mxUtils.bind(this, function(key, delta)
    {
        this.translateState(delta.state, delta.point.x, delta.point.y);
    }));

    this.deltas.visit(mxUtils.bind(this, function(key, delta)
    {
        this.revalidateState(delta.state, delta.point.x, delta.point.y, visitor);
    }));
};

/**
 * Function: translateState
 */
mxCellStatePreview.prototype.translateState = function(state, dx, dy)
{
    if (state != null)
    {
        var model = this.graph.getModel();

        if (model.isVertex(state.cell))
        {
            state.view.updateCellState(state);
            var geo = model.getGeometry(state.cell);

            // Moves selection cells and non-relative vertices in
            // the first phase so that edge terminal points will
            // be updated in the second phase
            if ((dx != 0 || dy != 0) && geo != null && (!geo.relative || this.deltas.get(state.cell) != null))
            {
                state.x += dx;
                state.y += dy;
            }
        }

        var childCount = model.getChildCount(state.cell);

        for (var i = 0; i < childCount; i++)
        {
            this.translateState(state.view.getState(model.getChildAt(state.cell, i)), dx, dy);
        }
    }
};

/**
 * Function: revalidateState
 */
mxCellStatePreview.prototype.revalidateState = function(state, dx, dy, visitor)
{
    if (state != null)
    {
        var model = this.graph.getModel();

        // Updates the edge terminal points and restores the
        // (relative) positions of any (relative) children
        if (model.isEdge(state.cell))
        {
            state.view.updateCellState(state);
        }

        var geo = this.graph.getCellGeometry(state.cell);
        var pState = state.view.getState(model.getParent(state.cell));

        // Moves selection vertices which are relative
        if ((dx != 0 || dy != 0) && geo != null && geo.relative &&
            model.isVertex(state.cell) && (pState == null ||
                model.isVertex(pState.cell) || this.deltas.get(state.cell) != null))
        {
            state.x += dx;
            state.y += dy;
        }

        this.graph.cellRenderer.redraw(state);

        // Invokes the visitor on the given state
        if (visitor != null)
        {
            visitor(state);
        }

        var childCount = model.getChildCount(state.cell);

        for (var i = 0; i < childCount; i++)
        {
            this.revalidateState(this.graph.view.getState(model.getChildAt(state.cell, i)), dx, dy, visitor);
        }
    }
};

/**
 * Function: addEdges
 */
mxCellStatePreview.prototype.addEdges = function(state)
{
    var model = this.graph.getModel();
    var edgeCount = model.getEdgeCount(state.cell);

    for (var i = 0; i < edgeCount; i++)
    {
        var s = state.view.getState(model.getEdgeAt(state.cell, i));

        if (s != null)
        {
            this.moveState(s, 0, 0);
        }
    }
};
